﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

namespace EsriGridToRaw
{
    class Program
    {
        public static int width = 129;
        public static int height = 129;
        public static Tile[,] tiles;
        public static Tile[,] ltiles;
        public static int tilesx, tilesy;

        static void Main(string[] args)
        {
            string rasterFilePath = System.Configuration.ConfigurationManager.AppSettings["RasterFilePath"];
            string worldFolderPath = System.Configuration.ConfigurationManager.AppSettings["WorldFolderPath"];
            LoadMap(rasterFilePath, worldFolderPath);
        }

        static void LoadMap(string name, string worldname)
        {
            if (!Directory.Exists(worldname))
            {
                Directory.CreateDirectory(worldname);
            }

            if (!Directory.Exists(worldname+"/tiles_hd"))
            {
                Directory.CreateDirectory(worldname + "/tiles_hd");
            }
            if (!Directory.Exists(worldname + "/tiles_ld"))
            {
                Directory.CreateDirectory(worldname + "/tiles_ld");
            }
            if (!Directory.Exists(worldname + "/texture_ld"))
            {
                Directory.CreateDirectory(worldname + "/texture_ld");
            }
            if (!Directory.Exists(worldname + "/texture_hd"))
            {
                Directory.CreateDirectory(worldname + "/texture_hd");
            }                  

            int terrainWidth = 500;
            int terrainHeight = 500;

            Console.WriteLine("Wczytywanie mapy...");
            FileLoader data = new FileLoader(name);

            // odczytanie danych

            string sh = "ncols";
            FparseXML.szukajsekcji(sh, data);
            Console.WriteLine("znaleziono sekcje " + sh + " na " + 0);
            terrainWidth = (int)FparseXML.parsujr(data);
            Console.WriteLine("wartosc to " + terrainWidth);

            sh = "nrows";
            FparseXML.szukajsekcji(sh, data);
            Console.WriteLine("znaleziono sekcje " + sh + " na " + 0);
            terrainHeight = (int)FparseXML.parsujr(data);
            Console.WriteLine("wartosc to " + terrainHeight);

            sh = "xllcorner";
            FparseXML.szukajsekcji(sh, data);
            Console.WriteLine("znaleziono sekcje " + sh + " na " + 0);
            Console.WriteLine("wartosc to " + FparseXML.parsujr(data));

            sh = "yllcorner";
            FparseXML.szukajsekcji(sh, data);
            Console.WriteLine("znaleziono sekcje " + sh + " na " + 0);
            Console.WriteLine("wartosc to " + FparseXML.parsujr(data));

            sh = "cellsize";
            FparseXML.szukajsekcji(sh, data);
            Console.WriteLine("znaleziono sekcje " + sh + " na " + 0);
            Console.WriteLine("wartosc to " + FparseXML.parsujr(data));

            sh = "NODATA_value";
            FparseXML.szukajsekcji(sh, data);
            Console.WriteLine("znaleziono sekcje " + sh + " na " + 0);
            Console.WriteLine("wartosc to " + FparseXML.parsujr(data));

            Console.WriteLine("mam odczytac " + terrainWidth * terrainHeight);

            tilesx = terrainWidth / width + 2;
            tilesy = terrainHeight / height + 2;
            Console.WriteLine("podzielono na " + tilesx + " x " + tilesy);

            // Utworzenie pliku ustawień świata
            TextWriter tw = new StreamWriter(worldname + "/world.txt");

            tw.WriteLine("TilesX " + tilesx);
            tw.WriteLine("TilesY " + tilesy);
            tw.WriteLine("TileWidth " + width);
            tw.WriteLine("TileHeight " + height);

            tw.Close();

            // Utworzenie kawalkow terenu

            tiles = new Tile[tilesx, tilesy];
            ltiles = new Tile[tilesx, tilesy];

            for (int i = 0; i < tilesx; i++)
            {
                for (int j = 0; j < tilesy; j++)
                {
                    tiles[i, j] = new Tile();
                    tiles[i, j].heightData = new float[width, height];

                    ltiles[i, j] = new Tile();
                    ltiles[i, j].heightData = new float[width/4+1, height/4+1];
                }
            }

            int itilex = 0;
            int offsetx = 0;
            int itiley = 0;
            int offsety = 0;
            float value = 0;

            for (int y = 0; y < terrainHeight; y++)
            {

                if (offsety == (height - 1))
                {
                    Console.WriteLine("rzadek " + itiley);
                    itiley++; offsety = 0;
                }

                offsetx = 0;
                itilex = 0;
                for (int x = 0; x < terrainWidth; x++)
                {
                    if (offsetx == (width - 1))
                    {
                        value = FparseXML.parsujr(data);
                        if (value == -9999) value = 0;

                        tiles[itilex, itiley].heightData[offsetx, offsety] = value;
                        if (itiley != 0 && offsety == 0)
                        {
                            tiles[itilex, itiley - 1].heightData[offsetx, height - 1] = value;
                        }
                        itilex++; offsetx = 0;
                    }
                    else
                    {
                        value = FparseXML.parsujr(data);
                        // zaniżamy nieco teren położony poniżej morza, żeby uniknąć clippingu z wodą
                        if (value == -9999) value = -0.1f;
                        else if (value == 0) value = -0.1f;
                    }

                    tiles[itilex, itiley].heightData[offsetx, offsety] = value;
                    if (itiley != 0 && offsety == 0)
                    {
                        tiles[itilex, itiley - 1].heightData[offsetx, height - 1] = value;
                    }
                    offsetx++;
                }
                offsety++;
            }

            Console.WriteLine("\nzapis\n ");
            // serializacja terenu do plikow
            for (int i = 0; i < tilesx; i++)
            {
                Console.WriteLine("rzadek " + i);
                for (int j = 0; j < tilesy; j++)
                {
                    for (int uu = 0; uu < width / 4 + 1; uu++)
                        for (int zz = 0; zz < height / 4 + 1; zz++)
                            ltiles[i, j].heightData[uu, zz] = tiles[i, j].heightData[uu * 4, zz * 4];
                    
                    // generowanie tekstury kafla
                    Bitmap txt = genTexture(tiles[i, j]);
                    txt.Save(worldname + "/texture_ld/tile_" + i + "_" + j+".png", System.Drawing.Imaging.ImageFormat.Png);

                    // zapis mapy terenu kafla
                    
                    Stream stream = null;
                    try
                    {
                        IFormatter formatter = new BinaryFormatter();
                        stream = new FileStream(worldname+"/tiles_hd/tile_"+i+"_"+j, FileMode.Create, FileAccess.Write, FileShare.None);
                        formatter.Serialize(stream, tiles[i, j].heightData);

                        stream = new FileStream(worldname + "/tiles_ld/tile_" + i + "_" + j, FileMode.Create, FileAccess.Write, FileShare.None);
                        formatter.Serialize(stream, ltiles[i, j].heightData);
                    }
                    catch
                    {
                        Console.WriteLine("BŁĄD - upewnij się, że podana ścieżka istnieje.");
                    }
                    finally
                    {
                        if (null != stream)
                            stream.Close();
                    }
                    
                }
            }

            Console.WriteLine("/nmegamapa/n ");
            // 
            //Bitmap txt2 = genMegaTexture();
            //txt2.Save(worldname + "mapa.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
        }

        public static Bitmap genMegaTexture()
        {
            Bitmap txt = new Bitmap((width - 1)*tilesx, (height - 1)*tilesy, PixelFormat.Format24bppRgb);
            Rectangle rect = new Rectangle(0, 0, txt.Width, txt.Height);
            BitmapData bmpData =
                txt.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, txt.PixelFormat);

            // Get the address of the first line.
            IntPtr ptr = bmpData.Scan0;

            // Declare an array to hold the bytes of the bitmap.
            int bytes = Math.Abs(bmpData.Stride) * txt.Height;
            byte[] rgbValues = new byte[bytes];

            // Copy the RGB values into the array.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

            // edit
            int i = 0, j = 0, uu = 0, zz = 0; 

            for (int c = 0; c < bytes;)
            {
                  int v = (int)tiles[i,j].heightData[uu, zz];
                  //Color kolor = TKolor.generujKolor1(v);
                  Color kolor = TKolor.generujKolor2(v);

                  rgbValues[c] = kolor.B; c++;
                  rgbValues[c] = kolor.G; c++;
                  rgbValues[c] = kolor.R; c++;

                  uu++; if (uu >= (width - 1)) { uu = 0; i++; }
                  if (i >= (tilesx)) { zz++; i = 0; uu = 0; }
                  if (zz >= (height - 1)) { zz = 0; uu = 0; i = 0; j++; }
                  if (j >= (tilesy)) { break; }
            }
            // Copy the RGB values back to the bitmap
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
            // Unlock the bits.
            txt.UnlockBits(bmpData);

            return txt;
        }

        public static Bitmap genTexture(Tile tile)
        {
            Bitmap txt = new Bitmap(width - 1, height - 1, PixelFormat.Format24bppRgb);
            Rectangle rect = new Rectangle(0, 0, txt.Width, txt.Height);
            BitmapData bmpData =
                txt.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, txt.PixelFormat);

            // Get the address of the first line.
            IntPtr ptr = bmpData.Scan0;

            // Declare an array to hold the bytes of the bitmap.
            int bytes = Math.Abs(bmpData.Stride) * txt.Height;
            byte[] rgbValues = new byte[bytes];

            // Copy the RGB values into the array.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

            // edit
            int counter = 0;
            for (int uu = 0; uu < (width - 1); uu++)
                for (int zz = 0; zz < (height - 1); zz++)
                {
                    //int v = (int)(tile.heightData[zz, uu] + tile.heightData[zz + 1, uu + 1] + tile.heightData[zz, uu + 1] + tile.heightData[zz + 1, uu]) / 4;
                    int v = (int)tile.heightData[zz, uu];

                    //Color kolor = TKolor.generujKolor1(v);
                    Color kolor = TKolor.generujKolor2(v);

                    rgbValues[counter] = kolor.B;
                    counter++;
                    rgbValues[counter] = kolor.G;
                    counter++;
                    rgbValues[counter] = kolor.R;
                    counter++;
                }

            // Copy the RGB values back to the bitmap
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
            // Unlock the bits.
            txt.UnlockBits(bmpData);

            return txt;
        }
    }
}
